﻿using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Globalization;
using System.Linq;
using System.Text;
using OpenLS.Core;
using OpenLS.Spreadsheet.Formats.JsonStructures;

namespace OpenLS.Spreadsheet.Formats
{
    public class JsonDataTableFormat
    {
        public JsonDataTableFormat(string strTableName)
        {
            _strTableName = strTableName;
        }

        #region Table rights management

        public bool IsPublic { get; set; }

        public bool IsReadOnly { get; set; }

        public bool AllowsInsert { get; set; }

        public bool AllowsUpdate { get; set; }

        public bool AllowsDelete { get; set; }

        #endregion


        public string Comment { get; set; }

        public string TableName
        {
            get { return _strTableName; }
        }

        private struct JsonDataTableColumn
        {
            public string ColumnName;
            public string ColumnType;

            /// <summary>
            /// All cell values used for table definition
            /// </summary>
            public List<object> cellValues;
            
            /// <summary>
            /// All cell types used for table definition
            /// </summary>
            public List<Format> cellFormats;
        }

        private Dictionary<int, JsonDataTableColumn> _tableColumns = new Dictionary<int, JsonDataTableColumn>();

        private string _strTableName;

        public int TableDisplayRowVisibleSize
        {
            get; set;
        }

        public int NumberOfRows
        {
            get
            {
                int maxCellNumber = 0;
                foreach (var keyValue in _tableColumns)
                {
                    var column = keyValue.Value;
                    maxCellNumber = Math.Max(maxCellNumber, column.cellValues.Count);
                }
                return maxCellNumber;
            }
        }

        public int NumberOfColumns
        {
            get { return _tableColumns.Count; }
        }

        public List<object> GetRow(int rowIndex)
        {
            List<object> rawValues = new List<object>();
            foreach (var keyValue in _tableColumns)
            {
                var column = keyValue.Value;
                rawValues.Add(column.cellValues[rowIndex]);
            }
            return rawValues;
        }

        private List<Format> Sort(List<Format> inputFormats)
        {
            inputFormats.Sort((a, b) =>
                {
                    if (a < b) return 1;
                    if (a.GetType() == b.GetType()) return 0;
                    return -1;
                }
                );
            return inputFormats;
        }

        private string GetCommonFormat(List<object> raws)
        {
            if (raws != null)
            {
                bool isDouble = true;
                Double outDoubleResult;
                foreach (var raw in raws)
                {
                    if (raw != null)
                    {
                        string strRaw = raw.ToString();
                        isDouble = isDouble &
                                   Double.TryParse(strRaw, NumberStyles.None,
                                                   System.Globalization.CultureInfo.CurrentCulture, out outDoubleResult);
                    }
                }
                if (isDouble)
                    return "number";

                bool isDate = true;
                DateTime outDateTimeResult;
                foreach (var raw in raws)
                {
                    string strRaw = raw.ToString();
                    isDate = isDate &
                             DateTime.TryParse(strRaw, System.Globalization.CultureInfo.CurrentCulture,
                                               DateTimeStyles.None, out outDateTimeResult);
                }
                if (isDate)
                    return "date";
            }

            return "string";
        }

        public string TableDescription
        {
            get
            {
                JsonStructures.JsonTableStructure currentTable = new JsonTableStructure();
                currentTable.Name = _strTableName;
                currentTable.IsPublic = this.IsPublic;
                currentTable.IsReadOnly = this.IsReadOnly;
                currentTable.AllowsInsert = this.AllowsInsert;
                currentTable.AllowsDelete = this.AllowsDelete;
                currentTable.AllowsUpdate = this.AllowsUpdate;

                currentTable.TableFields = new List<JsonTableField>();
                foreach (var aTableColumn in _tableColumns)
                {
                    JsonTableField aField = new JsonTableField();
                    aField.Id = aTableColumn.Value.ColumnName;
                    string strType = "string";
                    if (aTableColumn.Value.cellFormats != null)
                    {
                        List<Format> sortedFormats = Sort(aTableColumn.Value.cellFormats);
                        Format minimumFormat = sortedFormats[0];
                        if (minimumFormat is GeneralFormat)
                            strType = "string";
                        if (minimumFormat is DateTimeFormat)
                            strType = "date";
                        if (minimumFormat is NumberFormat)
                            strType = "number";
                    }
                    if (strType == "string")
                        strType = GetCommonFormat(aTableColumn.Value.cellValues);
                    aField.Type = strType;
                    currentTable.TableFields.Add(aField);
                }

                JsonTableDocument tableDoc = new JsonTableDocument();
                tableDoc.CurrentTable = currentTable;

                string jsonTableDefinition = Newtonsoft.Json.JsonConvert.SerializeObject(tableDoc);
                return jsonTableDefinition;
            }
        }

        public void AddColumn(string strHeader, string strType)
        {
            int columnCount = _tableColumns.Count;
            JsonDataTableColumn newColumn = new JsonDataTableColumn();
            newColumn.ColumnName = strHeader;
            _tableColumns.Add(columnCount, newColumn);
        }

        /// <summary>
        /// This function add an example of cell value. This collection is used to 
        /// auto-infer column types.
        /// </summary>
        /// <param name="columnIndex"></param>
        /// <param name="cellValue"></param>
        /// <param name="cellFormat"></param>
        public void AddCellValueType(int columnIndex, object cellValue, Format cellFormat)
        {
            if (_tableColumns.Count == 0)
                return;
            if (columnIndex >= _tableColumns.Count)
                return;
            JsonDataTableColumn aColumn = _tableColumns[columnIndex];
            if (aColumn.cellFormats == null)
            {
                aColumn.cellFormats = new List<Format>();
            }
            aColumn.cellFormats.Add(cellFormat);

            if (aColumn.cellValues == null)
            {
                aColumn.cellValues = new List<object>();
            }
            aColumn.cellValues.Add(cellValue);
            _tableColumns[columnIndex] = aColumn;
        }
    }
}
